﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using SimpleRest.Core.Extensions.Routes;
using SimpleRest.Core.Routing;
using System.Web.Routing;
using SimpleRest.Core.Extensions.Documentation;
using System.IO;
using System.Net;
using SimpleRest.Serialization;
using SimpleRest.WebTest.Entities;

namespace SimpleRest.WebTest
{
    public partial class Default : System.Web.UI.Page
    {
        private static Dictionary<string, SimpleRest.Core.Extensions.Documentation.Options> Options = null;

        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                if (TreeView1.Nodes.Count == 0)
                {
                    string baseUrl = string.Empty;
                    if (Request.IsSecureConnection)
                        baseUrl += "https://";
                    else
                        baseUrl += "http://";
                    baseUrl += this.Request["HTTP_HOST"] + "/" + this.Request.ApplicationPath + "/";

                    var operations = RouteUtility.GetRegisteredOperations().OrderBy(d => d.HttpMethod);
                    var treeRootNode = new TreeNode(baseUrl);
                    treeRootNode.ExpandAll();

                    foreach (var svc in RouteUtility.GetRegisteredServices().OrderBy(d => d.RouteUri))
                    {
                        var rootNode = new TreeNode("/" + svc.RouteUri);

                        foreach (var operation in operations.Where(d => d.ServiceType.FullName == svc.ServiceType.FullName))
                        {
                            rootNode.ChildNodes.Add(new TreeNode(operation.HttpMethod.ToUpper() + " " + operation.RouteUri));
                        }

                        foreach (var xsdRoute in RouteUtility.Routes.Where(d => d is XsdRoute && (d as XsdRoute).ServiceType.FullName == svc.ServiceType.FullName))
                        {
                            XsdRoute route = xsdRoute as XsdRoute;

                            rootNode.ChildNodes.Add(new TreeNode("GET " + route.Url));
                        }

                        foreach (var xsdRoute in RouteUtility.Routes.Where(d => d is DescriptionRoute && (d as DescriptionRoute).ServiceType.FullName == svc.ServiceType.FullName))
                        {
                            DescriptionRoute route = xsdRoute as DescriptionRoute;
                            var node = new TreeNode("OPTIONS " + route.Url);

                            if (Options == null)
                                Options = new Dictionary<string, SimpleRest.Core.Extensions.Documentation.Options>();
                            try
                            {
                                if (!Options.ContainsKey(route.Url))
                                    Options.Add(route.Url, GetOptions(GetCompleteUrl(baseUrl, route.Url)));
                            }
                            catch (Exception ex)
                            {
                                node.Text += " <i><font color='red'>Discovery Error</font></i>";
                                node.ToolTip = ex.Message;
                            }

                            rootNode.ChildNodes.Add(node);
                        }

                        rootNode.CollapseAll();
                        treeRootNode.ChildNodes.Add(rootNode);
                    }

                    this.TreeView1.Nodes.Add(treeRootNode);

                }
            }
            catch (Exception ex)
            {
                Response.Write(ex.ToString());
            }
        }

        private string GetCompleteUrl(string rootUrl, string routeUrl)
        {
            var pt1 = rootUrl.Replace('\\', '/').Trim().TrimEnd(new char[] { '/' });
            var pt2 = routeUrl.Trim().Replace('\\', '/').TrimStart(new char[] { '/' });
            return pt1 + "/" + pt2;
        }

        protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
        {
            ServiceDiv.Visible = true;
            TreeNode node = TreeView1.SelectedNode;
            string serviceUrlSelected = null;
            string operationUrlSelected = null;
            string methodSelected = null;

            if (node.Parent != null && node.Parent.Parent != null)
            {
                OperationDiv.Visible = true;
                serviceUrlSelected = node.Parent.Text;
                var split = node.Text.Split(new char[] { ' ' });
                operationUrlSelected = split[1];
                methodSelected = split[0];
            }
            else if (node.Parent == null)
            {
                OperationDiv.Visible = false;
                ServiceDiv.Visible = false;
            }
            else
            {
                foreach (TreeNode n in this.TreeView1.Nodes[0].ChildNodes)
                    n.Collapse();

                OperationDiv.Visible = false;
                serviceUrlSelected = node.Text;
                if (node.Expanded.HasValue && node.Expanded.Value)
                    node.Collapse();
                else
                    node.Expand();
            }

            ServiceNameLabel.Text = "";
            ServiceDescriptionLabel.Text = "";
            ServiceStatusCodesLabel.Text = "";
            ServiceHelpLinksLabel.Text = "";
            OperationDescriptionLabel.Text = "";
            OperationParametersLabel.Text = "";
            OperationHelpLinksLabel.Text = "";
            OperationStatusCodesLAbel.Text = "";

            if (!string.IsNullOrEmpty(serviceUrlSelected))
            {
                var key = serviceUrlSelected.TrimStart(new char[] { '/' });
                if (Options.ContainsKey(key))
                {
                    var option = Options[key];
                    var stuff = option.Resources.Select(d => d.BaseUri).ToList();
                    if (option != null)
                    {
                        ServiceNameLabel.Text = serviceUrlSelected;
                        ServiceDescriptionLabel.Text = option.Description;

                        if (option.HttpStatusCodes != null)
                            foreach (var statusCode in option.HttpStatusCodes)
                            {
                                ServiceStatusCodesLabel.Text += statusCode.StatusCode + (!string.IsNullOrEmpty(statusCode.ErrorCode) ? " (" + statusCode.ErrorCode + ")" : "") + " - " + statusCode.Description + "<br />";
                            }

                        if (option.Links != null)
                            foreach (var link in option.Links)
                            {
                                ServiceHelpLinksLabel.Text += "<a href = '" + link.Uri + "'>" + link.Description + "</a><br />";
                            }

                        if (option.Resources != null && !string.IsNullOrEmpty(operationUrlSelected))
                            foreach (var resource in option.Resources)
                            {
                                if (resource.BaseUri.Trim(new char[] { '/' }) == operationUrlSelected.Trim(new char[] { '/' }))
                                {
                                    bool isFound = false;
                                    if (methodSelected != null)
                                        foreach (var httpMethod in resource.HttpMethods)
                                        {
                                            if (httpMethod.Verb.ToUpper().Equals(methodSelected.ToUpper()))
                                            {
                                                OperationDescriptionLabel.Text = httpMethod.Description;
                                                OperationUriLabel.Text = httpMethod.FullUri;

                                                if (httpMethod.Parameters != null)
                                                    foreach (var param in httpMethod.Parameters)
                                                    {
                                                        OperationParametersLabel.Text += "<i>" + param.Type + "</i><b> " + param.Name + "</b>" + " (" + (param.IsOptional ? "Optional) - " : "Required) - ") + param.Description + "<br />";
                                                    }

                                                if (httpMethod.HttpStatusCodes != null)
                                                    foreach (var statusCode in httpMethod.HttpStatusCodes)
                                                    {
                                                        System.Net.HttpStatusCode code = (System.Net.HttpStatusCode)statusCode.StatusCode;
                                                        OperationStatusCodesLAbel.Text += statusCode.StatusCode + " " + code.ToString() + " - " + statusCode.Description + (!string.IsNullOrEmpty(statusCode.ErrorCode) ? " - (" + statusCode.ErrorCode + ")" : "") + "<br />";
                                                    }

                                                if (httpMethod.Links != null)
                                                    foreach (var link in httpMethod.Links)
                                                    {
                                                        OperationHelpLinksLabel.Text += "<a href = '" + link.Uri + "'>" + link.Description + "</a><br />"; ;
                                                    }

                                                isFound = true;
                                                break;
                                            }

                                            if (isFound)
                                                break;
                                        }
                                }
                            }
                    }
                }
            }

            if (ServiceNameLabel.Text == "")
                ServiceNameLabel.Text = "N/A";

            if (ServiceDescriptionLabel.Text == "")
                ServiceDescriptionLabel.Text = "N/A";

            if (ServiceStatusCodesLabel.Text == "")
                ServiceStatusCodesLabel.Text = "N/A";

            if (ServiceHelpLinksLabel.Text == "")
                ServiceHelpLinksLabel.Text = "N/A";

            if (OperationDescriptionLabel.Text == "")
                OperationDescriptionLabel.Text = "N/A";

            if (OperationParametersLabel.Text == "")
                OperationParametersLabel.Text = "N/A";

            if (OperationHelpLinksLabel.Text == "")
                OperationHelpLinksLabel.Text = "N/A";

            if (OperationStatusCodesLAbel.Text == "")
                OperationStatusCodesLAbel.Text = "N/A";
        }

        public static Options GetOptions(string uri)
        {
            if (string.IsNullOrEmpty(uri)) throw new ArgumentNullException("Uri");

            System.Net.HttpWebRequest request = System.Net.HttpWebRequest.Create(uri) as System.Net.HttpWebRequest;
            request.Method = "OPTIONS";
            request.ContentType = "application/json";
            request.Accept = "application/json";
            Stream dataStream = null;

            HttpWebResponse response = request.GetResponse() as HttpWebResponse;

            dataStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(dataStream);
            string responseFromServer = reader.ReadToEnd();

            reader.Close();
            dataStream.Close();
            response.Close();

            return DeSerialize<Options>(responseFromServer, uri);
        }

        private static byte[] ToBytes(object obj)
        {
            try
            {
                return System.Text.Encoding.UTF8.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(obj));
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Unable to serialize object specified.", ex);
            }
        }
        private static T DeSerialize<T>(string item, string uri)
        {
            try
            {
                return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(item);
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Unable to deserialize response.", ex);
            }
        }

    }
}